home *** CD-ROM | disk | FTP | other *** search
/ NeXT Enterprise Objects Framework 1.1 / NeXT Enterprise Objects Framework 1.1.iso / NextDeveloper / Examples / EnterpriseObjects / MasteringDetails / EOFExtensions.subproj / SavvyControllerDelegate.m < prev    next >
Encoding:
Text File  |  1995-02-18  |  10.0 KB  |  249 lines

  1. /* ModelSavyController.m created by cfeder on Tue 01-Nov-1994 */
  2.  
  3. /*
  4.    Interesting master-detail relationships:
  5.    M     Detail   Insert rule              Delete                 Types
  6.    PK -> PK       D->PK := M->PK           delete D               to-one
  7.    FK -> PK       M->FK := D->PK           M->FK=NULL             to-one
  8.    PK -> FK       D->FK := M->PK           Delete D               to-many
  9.    Many to many   create link obj          Delete link            to-many
  10. */
  11. #import "SavvyControllerDelegate.h"
  12. #import "RelationshipKeySetter.h"
  13. #import "ResetRelationships.h"
  14. #import "KeyGenerator.h"
  15. #import "ValueForKey.h"
  16.  
  17.  
  18. @interface EODetailDatabaseDataSource (databaseChannel)
  19. - (EODatabaseChannel *)databaseChannel;
  20. @end
  21.  
  22. @implementation EODetailDatabaseDataSource (databaseChannel)
  23. - (EODatabaseChannel *)databaseChannel
  24. {
  25.     return [[self masterDataSource] databaseChannel];
  26. }
  27. @end
  28.  
  29.  
  30.  
  31. @implementation SavvyControllerDelegate
  32.  
  33. - (EORelationship *)detailRelationshipForController:controller;
  34. {
  35.     // Look up
  36.     id dataSource = [controller dataSource];
  37.     if ([dataSource respondsToSelector:@selector(masterObject)]) {
  38.         EOEntity *entity = [dataSource entity];
  39.         id masterObject = [dataSource masterObject];
  40.         EOEntity *masterEntity = [[entity model] entityForObject:masterObject];
  41.         return [masterEntity relationshipNamed:[dataSource detailKey]];        
  42.     }
  43.     return nil;
  44. }
  45.  
  46. - (void)updateDetailObjectsForController:(EOController *)controller
  47. {
  48.     EORelationship *relationship = [self detailRelationshipForController:controller];
  49.     if (relationship) {
  50.         // if this is a detail controller, then we may have to update
  51.         // the relationship property of the master EO, since we
  52.         // may have supressed insert and delete operations bound
  53.         // for the detailDataSource
  54.         id masterObject = [(id)[controller dataSource] masterObject];
  55.         NSArray *objects = [controller allObjects];
  56.         if ([relationship isToMany]) {
  57.             objects = [[[NSArray alloc] initWithArray:objects] autorelease];
  58.         } else {
  59.             objects = [objects count] ? [objects objectAtIndex:0] : [EONull null];
  60.         }
  61.         [masterObject takeValue:objects forKey:[relationship name]];
  62.     }
  63. }
  64.  
  65.  
  66.  
  67. // EOController delegation methods
  68.  
  69. - (BOOL)controller:(EOController *)controller willInsertObject:object atIndex: (unsigned)newIndex;
  70. {
  71.     // Handles primary key assignment for newly inserted objects.
  72.     EOEntity *entity = [(id)[controller dataSource] entity];
  73.     [object assignPrimaryKeyIfNotAlreadyPresentForEntity:entity];
  74.  
  75.     return YES;
  76. }
  77.  
  78. // Handles insertions into a detail controller.  The tricky part here is
  79. // that if we assign a new department in the detail controller for
  80. // an employee, we don't want to actually add the department to the
  81. // database (it's already there).  Instead, we want to update the foreign
  82. // key in the master object to point at the new department.
  83. // Similarly if we insert a project in the toProjects (many-to-many) detail
  84. // controller, we don't want to insert the project, but rather a "link"
  85. // object in the intermediate table emp_projects that joins employee and
  86. // project.
  87. - (EODataSourceOperationDelegateResponse)controller:(EOController *)controller
  88.     willInsertObject:object
  89.     inDataSource:dataSource;
  90. {
  91.     EODataSourceOperationDelegateResponse response = EOPerformDataSourceOperation;
  92.     EORelationship *relationship = [self detailRelationshipForController:controller];
  93.  
  94.     // This stuff only works if our dataSource is an EODetailDatabaseDataSource.
  95.     // Otherwise we just pass the operation through
  96.     if (relationship) {
  97.         id masterObject = [dataSource masterObject];
  98.         id modifiedObject;
  99.         EODatabaseDataSource *masterDataSource =  [dataSource masterDataSource];
  100.  
  101.         // Invoke the magic of the ModelRelExtensions to figure out what to
  102.         // do for this relationship
  103.         modifiedObject = [relationship updateKeysForSourceObject:masterObject destinationObject:object];
  104.  
  105.         if (modifiedObject == object) {
  106.             modifiedObject = nil;
  107.         } else if (modifiedObject == masterObject) {
  108.             // we need to update this one as well
  109.             NSLog(@"Updating master object '%@'", masterObject);
  110.             if (![masterDataSource updateObject:masterObject])
  111.                 response = EORollbackDataSourceOperation;  // put up error?
  112.         } else {
  113.             // link object that we need to insert
  114.             NSLog(@"Inserting link object '%@'", modifiedObject);
  115.             if (![masterDataSource insertObject:modifiedObject])
  116.                 response = EORollbackDataSourceOperation;  // put up error?
  117.         }
  118.  
  119.         if (modifiedObject) {
  120.             // do we really need to insert this object?  Or did the insert just indicate
  121.             // a reference to an existing object?
  122.             EODatabaseContext *context = [[dataSource databaseChannel] databaseContext];
  123.             if ([context snapshotForObject:object])
  124.                 response = EODiscardDataSourceOperation;
  125.         }
  126.     }    
  127.     if (response == EOPerformDataSourceOperation)
  128.         NSLog(@"Allowing insert of object '%@'", object);
  129.     else {
  130.         NSLog(@"Suppressing insert of object: '%@'", object);
  131.     }
  132.     return response;
  133. }
  134.  
  135. - (EODataSourceOperationDelegateResponse)controller:(EOController *)controller
  136.     willDeleteObject:object
  137.     inDataSource:dataSource;
  138. {
  139.     /*Deletion of detail record may dictate one of the following...
  140.        M     Detail   Insert rule              Delete                 Types
  141.        PK -> PK       D->PK := M->PK           delete D               to-one
  142.        FK -> PK       M->FK := D->PK           M->FK=NULL             to-one
  143.        PK -> FK       D->FK := M->PK           Delete D               to-many
  144.        Many to many   create link obj          Delete link            to-many
  145.     */
  146.     EODataSourceOperationDelegateResponse response = EOPerformDataSourceOperation;
  147.     EORelationship *relationship = [self detailRelationshipForController:controller];
  148.  
  149.     // This stuff only works if our dataSource is an EODetailDatabaseDataSource.
  150.     // Otherwise we just pass the operation through
  151.     if (relationship) {
  152.         EOEntity *entity = [relationship destinationEntity];
  153.         id masterObject = [dataSource masterObject];
  154.         id modifiedObject;
  155.  
  156.         // Invoke the magic of the ModelRelExtensions to figure out what to
  157.         // do for this relationship
  158.         modifiedObject = [relationship updateKeysForDeleteOfDestinationObject:object fromSourceObject:masterObject];
  159.  
  160.         if (modifiedObject == object) {
  161.             // just go ahead and delete this one
  162.         } else if (modifiedObject == masterObject) {
  163.             // we need to update the master to reflect the delete
  164.             // and leave this object alone
  165.             response = EODiscardDataSourceOperation;
  166.             NSLog(@"Updating master object '%@'", masterObject);
  167.             if (![dataSource updateObject:masterObject])
  168.                 response = EORollbackDataSourceOperation;  // put up error?
  169.         } else {
  170.             // we need to delete the link object
  171.             // and leave this object alone
  172.             EODatabaseContext *context = [[dataSource databaseChannel] databaseContext];
  173.             EODatabase *database = [context database];
  174.             EOEntity *linkEntity = [[entity model] entityForObject:modifiedObject];
  175.             NSDictionary *pkDict;
  176.             id registeredObject;
  177.  
  178.             response = EODiscardDataSourceOperation; // leave destination object alone
  179.  
  180.             // to delete the object we need to make sure that it's in the uniquing
  181.             // tables.  Check if it's already there, and if not, record this one
  182.             pkDict = [modifiedObject valuesForKeys:[linkEntity primaryKeyAttributeNames]];
  183.             registeredObject = [context objectForPrimaryKey:pkDict entity:linkEntity];
  184.             if (!registeredObject) {
  185.                 [database recordObject:modifiedObject primaryKey:pkDict
  186.                              snapshot:pkDict];
  187.                 registeredObject = modifiedObject;
  188.             }
  189.             NSLog(@"Deleting link object '%@'", registeredObject);
  190.             if (![[dataSource masterDataSource] deleteObject:registeredObject])
  191.                 response = EORollbackDataSourceOperation;  // put up error?
  192.         }
  193.     }
  194.     if (response == EOPerformDataSourceOperation)
  195.         NSLog(@"Allowing delete of object '%@'", object);
  196.     else {
  197.         NSLog(@"Suppressing delete of object: '%@'", object);
  198.     }
  199.     return response;
  200. }
  201.  
  202. - (void)controllerDidSaveToDataSource:(EOController *)controller
  203. {
  204.     [self updateDetailObjectsForController:controller];
  205. }
  206.  
  207. @end
  208.  
  209.  
  210. // This is probably evil.
  211. // This code checks modifications to foreign key properties in an EO, and
  212. // reassigns any corresponding relationship pointers using methods in
  213. // ResetRelationships.h.
  214. //
  215. // THIS CODE IS NOT ACTUALLY USED IN THE EXAMPLE PROGRAM, but is here as
  216. // an illustration.
  217. @implementation SavvyControllerDelegate (ResetRelationshipsForChangedKeyProps)
  218. static NSArray *changedKeys = nil;
  219.  
  220. - (NSDictionary *)controller:(EOController *)controller willSaveEdits: (NSDictionary *)edits toObject:object
  221. {
  222.     // update any related relationship properties (in case we update foreign keys)
  223.     changedKeys = [edits allKeys];
  224.     return edits;
  225. }
  226.  
  227. - (void)controller:(EOController *)controller didSaveToObject:object
  228. {
  229.     EODatabaseDataSource *ds = (EODatabaseDataSource *)[controller dataSource];
  230.     EOEntity *entity = [ds entity];
  231.  
  232.     // update relationships for key changes
  233.     [[ds databaseChannel] updateRelationshipsInObject:object forModifiedAttributes:[entity attributesNamed:changedKeys]];
  234.  
  235.     // update keys for relationship changes
  236.     {
  237.         int i, count = [changedKeys count];
  238.         for(i=0; i<count; i++) {
  239.             NSString *key = [changedKeys objectAtIndex:i];
  240.             EORelationship *relationship = [entity relationshipNamed:key];
  241.             if (relationship) {
  242.                 [relationship updateKeysForSourceObject:object
  243.                                       destinationObject:[(NSObject *)object valueForKey:key]];
  244.             }
  245.         }
  246.     }
  247. }
  248. @end
  249.